package controllers;

import java.util.EnumSet;
import java.util.UUID;

import models.SmartsheetToken;
import models.User;
import play.Logger;
import play.mvc.Controller;
import play.mvc.Result;
import utils.Key;

import com.smartsheet.api.Smartsheet;
import com.smartsheet.api.SmartsheetBuilder;
import com.smartsheet.api.UserResources;
import com.smartsheet.api.models.UserProfile;
import com.smartsheet.api.oauth.AccessScope;
import com.smartsheet.api.oauth.AuthorizationResult;
import com.smartsheet.api.oauth.OAuthFlow;
import com.smartsheet.api.oauth.OAuthFlowBuilder;
import com.smartsheet.api.oauth.Token;

public class OAuthController extends Controller {
	
	// from the environment
	public static final String SMARTSHEET_CLIENT_ID = System.getenv("SMARTSHEET_CLIENT_ID");
	public static final String SMARTSHEET_CLIENT_SECRET = System.getenv("SMARTSHEET_CLIENT_SECRET");
	public static final String SMARTSHEET_REDIRECT_URL = System.getenv("SMARTSHEET_REDIRECT_URL");

	/**
	 * Start Smartsheet OAuth flow, and redirect to Smartsheet OAuth authorization URL.
	 * @return Result, redirected to Smartsheet authorization URL
	 * @throws Exception
	 */
    public static Result loginWithOauth() throws Exception {
    	OAuthFlow ssAuthFlow = getAuthFlow();
		
		// state maybe sessionId
    	EnumSet<AccessScope> enumSet = EnumSet.of(AccessScope.READ_SHEETS);
    	enumSet.add(AccessScope.WRITE_SHEETS);
    	enumSet.add(AccessScope.ADMIN_USERS);
    	
		// generate random ID and pass it as a state
		String state = UUID.randomUUID().toString();
		session().put(Key.OAUTH_STATE, state);
		
		String authUrl = ssAuthFlow.newAuthorizationURL(enumSet, state);
		Logger.info("authUrl: "+authUrl);
		return redirect(authUrl);
    }
    
    /**
     * Handle the OAuth callback from Smartsheet. On successfull callback, send a request 
     * to get access token. The returned access token is save in SmartsheetToken table and
     * used to access Smartsheet REST API.
     * @return Result, redirected to Dashboard page
     * @throws Exception
     */
    public static Result oauthCallback() throws Exception {
    	String state = request().getQueryString("state");
    	String error = request().getQueryString("error");
    	String expectedState = session(Key.OAUTH_STATE);
    	// check error
    	if (error != null || expectedState != null && !expectedState.equals(state)) {
    		Application.setFlash("danger", "Signing in to Smartsheet had error, please try again");
    	} else {	// successful
	    	AuthorizationResult authResult = new AuthorizationResult();
	    	authResult.setCode(request().getQueryString("code"));
	    	authResult.setState(request().getQueryString("state"));
	    	authResult.setExpiresInSeconds(Long.parseLong(request().getQueryString("expires_in")));
	    	
	    	// request smartsheet access token
	    	// catch exception, redirect to login page on error
	    	OAuthFlow ssAuthFlow = getAuthFlow();
	    	Token token = ssAuthFlow.obtainNewToken(authResult);
	    	Logger.info("accessToken: "+token.getAccessToken());
	
	    	Smartsheet smartsheet = new SmartsheetBuilder().setAccessToken(token.getAccessToken()).build();
	    	
	    	// get user info
			UserResources userResources = smartsheet.users();
			UserProfile profile = userResources.getCurrentUser();
			
			// add user to database if not exists
			User currentUser = User.findByEmail(profile.getEmail());
			if (currentUser == null) {
				currentUser = new User();
				currentUser.updateFromProfile(profile);
				User.saveOrUpdate(currentUser);
				Logger.info("added new user: "+currentUser.userId);
			}
			
			Logger.info("currentUser token: "+currentUser.token);
			SmartsheetToken ssToken = currentUser.token;
			if (ssToken == null) {
				ssToken = new SmartsheetToken();
				currentUser.token = ssToken;
			}
			// save or update token
			ssToken.updateFromToken(token);
			ssToken.user = currentUser;
			User.saveOrUpdate(currentUser);
			SmartsheetToken.saveOrUpdate(ssToken);	// this may not be necessary
			Logger.info("saved token: "+ssToken.smartsheetTokenId);
			
			// save user.id to session
			session(Key.USER_ID, currentUser.userId.toString());
			Application.setFlash("success", "Signing into SmartSign successfully, explore SmartSign!");
    	}
    	// redirect to Dashboard
		return redirect(routes.Application.index());
    }
    
    /**
     * create and return Smartsheet OAuthFlow object to get or refresh access token.
     * @return OAuthFlow
     */
    public static OAuthFlow getAuthFlow() {
		OAuthFlowBuilder builder = new OAuthFlowBuilder();
    	EnumSet<AccessScope> enumSet = EnumSet.of(AccessScope.READ_SHEETS);
    	enumSet.add(AccessScope.WRITE_SHEETS);
    	OAuthFlow ssAuthFlow = builder.setClientId(SMARTSHEET_CLIENT_ID)
    				.setClientSecret(SMARTSHEET_CLIENT_SECRET)
    				.setRedirectURL(SMARTSHEET_REDIRECT_URL).build();
    	return ssAuthFlow;
    }
}
